#pragma once 

#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/types.h>
#include <pthread.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <iostream>

#include "Connect.hpp"
#include "Tool.hpp"
#include "UserManager.hpp"
#include "MessagePool.hpp"

#define MAX_ROUND 10
#define THREAD_COUNT 1
class TcpConnect
{

    public:
        TcpConnect()
        {
            server_ = NULL;
        }
        
        ~TcpConnect()
        {

        }
        
        void SetSockfd(int fd)
        {
            new_sock_ = fd;
        }

        void SetServer(void* server)
        {
            server_ = server;
        }

        void* GetServer()
        {
            return server_;
        }

        int GetSockfd()
        {
            return new_sock_;
        }
    private:
        int  new_sock_;
        void* server_;
};


//创建一个服务类，实现操作
class ChatServer
{
    public:
        ChatServer()
        {   
            tcp_sock_= -1;
            user_manager_ = NULL;
            udp_sock_ = -1;
            memset(con_tid,'\0',THREAD_COUNT*sizeof(pthread_t));
            memset(pro_tid,'\0',THREAD_COUNT*sizeof(pthread_t));
            msg_pool_ = NULL;
        }
        ~ChatServer()
        {

        }
        //初始化变量，创建套接字，绑定，监听。
        //默认端口
        int Init(uint32_t port_tcp = TCP_PORT)
        {
            //创建消息池
            msg_pool_ = new MsgPool(1024);
            if(msg_pool_ == NULL)
            {   
                LOG(ERROR,"init MsgPool faild") << endl;
                return -1;
            }
            
            //创建套接字
            tcp_sock_ = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
            //创建失败
            if(tcp_sock_ < 0)
            {
                return -1;
            }
            //创建成功，绑定
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_port = htons(port_tcp);
            addr.sin_addr.s_addr = inet_addr("0.0.0.0");//所有网卡地址
            int ret = bind(tcp_sock_,(struct sockaddr*)&addr,sizeof(addr));
            if(ret < 0)
            {
                return -2;
            }

            //绑定成功后进行监听
            ret = listen(tcp_sock_,5);
            if(ret < 0)
            {
                return -3;
            }
            
            LOG(WARNING,"bind sucess")<<endl;
            
            //初始化用户管理，消息池实例化
            user_manager_ = new UserManager();
            if(!user_manager_)
            {
                LOG(ERROR,"new UserManager faild");
                return -4;
            }
            
            //创建udp套接字
            udp_sock_ = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
            if(udp_sock_< 0)
            {
                LOG(ERROR,"create_udp_sock faild") << endl;
                return -1;
            }
            ret  = bind(udp_sock_,(struct sockaddr*)&addr,sizeof(addr));
            if(ret < 0)
            {
                LOG(ERROR,"bind faild") << endl;
                return -1;
            }
            LOG(INFO,"udp_sock_ port is :") << UDP_PORT << endl; 
            LOG(INFO,"Server init success") << endl;
            return 0;
            
        }
        //启动线程
        int Start()
        {
            //启动udp线程，udp线程分为两类线程，一类是生产线程，一类是消费线程
            //1.生产线程：把消息接收放到消息池中
            //2.消费线程：把消息从消息池发送到在线用户上
            for(int i = 0;i < THREAD_COUNT;i++)
            {
                int ret = pthread_create(&pro_tid[i],NULL,ProductStart,(void*)this);
                if(ret < 0)
                {
                    LOG(ERROR,"pthread_create faild");
                    return -1;
                }
                ret = pthread_create(&con_tid[i],NULL,ConsumeStart,(void*)this);
                if(ret < 0)
                {
                    LOG(ERROR,"pthread_create faild");
                    return -1;
                }
                
            }
            
            
            //tcp线程和udp线程，先创建udp线程，再创建tcp线程
            //因为tcp用到了accept，如果接收不到会阻塞
            
            //tcp线程的创建  接收第一次是判断注册还是登录
            //接收第二次是接收数据
            struct sockaddr_in peer_addr;
            socklen_t len = sizeof(peer_addr);
            while(1)
            {
                uint32_t new_sock_ = accept(tcp_sock_,(struct sockaddr*)&peer_addr,&len);
                //表示没有接收到
                if(new_sock_ < 0)
                {
                    continue;
                }
                TcpConnect* tc = new TcpConnect();
                tc->SetSockfd(new_sock_);
                tc->SetServer((void*)this);
                //成功接收到,创建tcp线程
                pthread_t tid;
                int ret = pthread_create(&tid,NULL,RegisterLogin,(void*)tc);
                //创建失败
                if(ret < 0)
                {
                    close(new_sock_);
                    delete tc;
                    continue;
                }
            
            }
        }
    private:
        static void* ProductStart(void* arg)
        {
            pthread_detach(pthread_self());
            ChatServer* cs = (ChatServer*)arg;
           //存放到消息池里 
           while(1)
           {
                cs->RecvMsg();
           }
        }

        static void* ConsumeStart(void* arg)
        {
            pthread_detach(pthread_self());
            ChatServer* cs = (ChatServer*)arg;
            //从消息池发送到在线客户端
            while(1)
            {
                cs->SendMsg();
            }
        }

        static void* RegisterLogin(void* arg)
        {
            //分离出来处理
            pthread_detach(pthread_self());

            //判断第一个tcp是登录还是注册
            //接收约定的数据格式
            //这里要传递new_tcp
            TcpConnect* tc =(TcpConnect*)arg;
            ChatServer* cs =(ChatServer*)tc->GetServer();
            char resq_ = -1;//第一个传递tcp，请求是注册还是登录
            ssize_t recv_size  = recv(tc->GetSockfd(),&resq_,1,0);
            if(recv_size < 0)//接收失败
            {
                close(tc->GetSockfd());
                return NULL;
            }
            else if(recv_size == 0)//断开了连接
            {
                close(tc->GetSockfd());
                return NULL;;
            }
            uint32_t id_;
            int resp_status = -1;
            //表明接收成功
            switch(resq_)
            {
                case Register:
                    {
                        //处理注册
                        //拿一下这个id值
                        resp_status = cs->DealRegister(tc->GetSockfd(),&id_);
                        LOG(INFO,"cs->DealRegister") << endl;
                        break;
                    }

                case Login:
                    {
                        resp_status = cs->DealLogin(tc->GetSockfd(),&id_);
                        LOG(INFO,"cs->DealLogin") << endl;
                        break;
                    }
            }
            
            
            //服务端回复客户端
            //响应
            ReplyInfo ri;
            ri.resp_status_ = resp_status;
            ri.id_ = id_;
            LOG(INFO,"resp_status is ")<< ri.resp_status_ <<endl;
            LOG(INFO,"id is ")<< ri.id_ << endl;
            //发送给客户端
            int max_round = MAX_ROUND; 
            while(max_round > 0)
            {
                ssize_t send_size = send(tc->GetSockfd(),&ri,sizeof(ri),0);
                //标识发送成功
                if(send_size >= 0)
                {
                    LOG(INFO,"send_size success") << endl;
                    break;
                }
                else 
                {
                    LOG(WARNING,"send_size faild") << endl;
                    max_round--;
                }
            }
            close(tc->GetSockfd());
            delete tc;
            tc = NULL;
            return NULL;
        }

    private:
        int SendMsg()
        {
            //把消息池里的消息发送给在线用户
            string msg;
            //msg_pool_->PopMsg(&msg);
            vector<UserInfo> vec;
            user_manager_->GetOnlineUser(&vec);

            for(size_t i = 0; i < vec.size(); i++)
            {
                SendUdpMsg(msg,vec[i].GetAddrInfo(),vec[i].GetAddrLen());
                cout << i << ":" << msg << "==>" << inet_ntoa(vec[i].GetAddrInfo().sin_addr) << endl;
            }
            return 0;
            
        }

        int SendUdpMsg(const string& msg,struct sockaddr_in addr,socklen_t addr_len)
        {
            ssize_t send_size = sendto(udp_sock_,msg.c_str(),msg.size(),0,(struct sockaddr*)&addr,addr_len);
            if(send_size < 0)
            {
                LOG(ERROR,"sendto faild");
                return -1;
            }
            return 0;
        }
        int RecvMsg()
        {
            //接收udp数据
            //判断该用户是不是第一次发送udp数据
            //    如果是，需要保存该用户的udp地址，并且将该用户放到在线用户列表中
            //    如果不是，说明已经是老用户了，之前保存过该用户的udp地址
            //把消息放到消息池中
            char buff[UDP_DATA_MAX_LEN] = {0};
            struct sockaddr_in peer_addr;
            socklen_t peer_addr_len = sizeof(peer_addr);
            ssize_t recv_size = recvfrom(udp_sock_,buff,sizeof(buff)-1,0,(struct sockaddr*)&peer_addr,&peer_addr_len);
            if(recv_size < 0)
            {
                LOG(ERROR,"recvfrom faild") << endl;
                return -1;
            }
            UdpMsg um;
            string msg;
            msg.assign(buff,strlen(buff));
            um.deserialize(msg);
            //判断该用户是否是登录用户
            int ret = user_manager_->IsLogin(um.id_,peer_addr,peer_addr_len);
            if(ret < 0)
            {
                LOG(ERROR,"IsLogin faild");
                return -1;
            }

            //正常逻辑
            msg_pool_->PushMsg(msg);
            return 0;
            
        }
        int DealRegister(int new_sock_,uint32_t* id)
        {
            struct RegisterInfo ri;
            //接收第二个数据包
            ssize_t recv_size = recv(new_sock_,&ri,sizeof(ri),0);
            //接收失败
            if(recv_size < 0)
            {
                close(new_sock_);
                return -1;
            }
            //对端断开
            else if(recv_size == 0)
            {
                close(new_sock_);
                return -2;
            }
            //正常接收
            //用户管理模块来处理
            
            int ret = user_manager_->DealRegister(ri.name_,ri.school_,ri.passwd_,id);
            if(ret < 0)
            {
                LOG(ERROR,"Rigister faild");
                close(new_sock_);
                return REGISTER_FAILED;
            }
            else 
            {
                return REGISTER_SUCCESS;
            }
        }
        int DealLogin(int new_sock,uint32_t* id)
        {
            LoginInfo li;
            ssize_t recv_size = recv(new_sock,&li,sizeof(li),0);
            if(recv_size < 0)
            {
                LOG(ERROR,"deallogin recv faild") << endl;
                return -1;
            }
            else if(recv_size == 0)
            {
                LOG(ERROR,"peer port shutdown") << endl;
                close(new_sock);
                return -2;
            }
           LOG(INFO,"recv login info  success") << endl;
            *id = li.id_;
            cout << "passwd:" << li.passwd_ << endl;
            cout << "id:" << li.id_ << endl;
            //接收成功
            //用户管理处理登录
            int ret = user_manager_->DealLogin(li.id_,li.passwd_);
            if(ret < 0)
            {
                LOG(ERROR,"LOGIN_FAILD") << endl;
                return LOGIN_FAILED;
            }
            LOG(INFO,"LOGIN_SUCCESS") << endl; 
            return LOGIN_SUCCESS;
        }
    private:
        //tcp套接字
        int tcp_sock_;
        //udp套接字
        int udp_sock_;
        //用户管理模块实例化
        UserManager* user_manager_;
        //消费线程句柄
        pthread_t con_tid[THREAD_COUNT];
        //生产线程句柄
        pthread_t pro_tid[THREAD_COUNT];
        MsgPool* msg_pool_;
};
